package bingo.component.dhtmlxgrid;

import java.io.BufferedOutputStream;
import java.io.BufferedReader;
import java.io.CharArrayWriter;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.lang.reflect.InvocationTargetException;
import java.text.ParseException;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import net.bingosoft.common.BeanHelper;
import net.bingosoft.common.ObjectRegistry;
import net.bingosoft.common.data.BaseDao;
import net.bingosoft.common.data.Pager;
import net.bingosoft.common.data.SQLProvider;
import net.bingosoft.common.data.SQLProvider.DynamicSQL;
import net.bingosoft.common.json.JSONObject;
import net.bingosoft.common.struts.ActionContext;
import net.bingosoft.common.util.XMLUtil;
import net.sf.jxls.transformer.XLSTransformer;

import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.dom4j.Document;
import org.dom4j.Element;

import bingo.component.common.IDhtmlxGridDataProcessor;
import bingo.component.common.IRequestParamPreprocessor;
import bingo.component.dhtmlxgrid.config.Column;
import bingo.component.dhtmlxgrid.config.Head;
import bingo.component.dhtmlxgrid.config.XMLSerializer;

/**
 * Jun 18, 2008 4:48:14 PM Author:yangdongyu(yangdy@bingosoft.net) ReadMe:
 * ChangeLog:
 */
@SuppressWarnings("unchecked")
public class GridDataLoaderServlet extends HttpServlet {

	private static final long serialVersionUID = 1L;
	private static final Log log = LogFactory.getLog(GridDataLoaderServlet.class);
	private final static int buf_size = 4096;

	@Override
	protected void doGet(HttpServletRequest req, HttpServletResponse resp)
			throws ServletException, IOException {
		String fileFullName = req.getParameter("fileFullName");
		String outputFileName = req.getParameter("exportExcelName");
		try {
			int byteread = 0;
			InputStream inStream = new FileInputStream(fileFullName);
			OutputStream fs = resp.getOutputStream();
			byte[] buffer = new byte[1444];
			resp.reset();
			resp.setHeader("Content-Type", "application/octet-stream");
			resp.setHeader("Content-Disposition", "attachment;filename="
					+ (StringUtils.isEmpty(outputFileName) ? fileFullName
							: outputFileName));
			resp.setHeader("Connection", "close");
			while ((byteread = inStream.read(buffer)) != -1) {
				fs.write(buffer, 0, byteread);
			}
			fs.flush();
			fs.close();
			inStream.close();
		} catch (Exception e) {
			log.error("读取列表导出的excel出错，文件名为：" + fileFullName, e);
		}
	}

	@Override
	protected void doPost(HttpServletRequest req, HttpServletResponse resp)
			throws ServletException, IOException {
		// 获取sql语句的Id
		String sqlKey = req.getParameter("sqlId");
		String envSqlId = req.getParameter("envSqlId");
		// 获取header配置文件的位置
		InputStream configFile = getServletContext().getResourceAsStream("/" + req.getParameter("configFileName"));

		// 是否是for导出
		String isForExport = req.getParameter("isForExport");

		// 获取查询条件、排序条件和分页要求等信息
		JSONObject json = readJSONFormRequest(req);
		if (json == null) {
			log.error("没有获取到request里的参数");
			return;
		}

		try {
			ActionContext.init(req, resp);
			Map<String, Object> requestParams = getParameterMap(req, json);

			// request参数预处理
			requestParamProcess(requestParams);

			if (StringUtils.isEmpty(isForExport)) {
				String gridXmlData = loadXMLData(sqlKey, envSqlId, configFile,json, requestParams);
				renderXML(resp, gridXmlData);
			} else {
				InputStream excelTemplate = getServletContext().getResourceAsStream("/" + req.getParameter("excelTemplateName"));

				DataRows dataRows = getDatas(sqlKey, null, json, requestParams, false);
				XLSTransformer xlsTransformer = new XLSTransformer();
				Map<String, Object> bean = new HashMap<String, Object>();
				bean.put("datas", dataRows.getRows());
				String fileFullName = getInitParameter("exportExcelPath")
						+ UUID.randomUUID().toString() + ".xls";
				HSSFWorkbook hssfWorkbook = xlsTransformer.transformXLS(
						excelTemplate, bean);
				OutputStream os = new BufferedOutputStream(
						new FileOutputStream(fileFullName));
				try {
					hssfWorkbook.write(os);
					os.flush();
				} finally {
					os.close();
					excelTemplate.close();
					resp.getWriter().println(fileFullName);
				}
			}
		} catch (Exception e) {
			log.error("获取或导出DhtmlxGrid数据时发生错误", e);
		}finally{
			ActionContext.destory();
		}
	}

	/**
	 * 从request中获取请求参数，并以Map的形式返回
	 * 
	 * @param request
	 * @return
	 */
	@SuppressWarnings("unchecked")
	private Map<String, Object> getParameterMap(HttpServletRequest request, JSONObject json) {
		Enumeration<String> names = request.getParameterNames();
		Map<String, Object> map = new HashMap<String, Object>();
		while (names.hasMoreElements()) {
			String name = (String) names.nextElement();
			String[] s = request.getParameterValues(name);
			if (s != null) {
				if (s.length > 1) {
					map.put(name, s);
				} else {
					map.put(name, s[0]);
				}
			}
		}
		
		// 获取排序信息
		JSONObject orderInfo = json.optJSONObject("orderBy");
		if (orderInfo != null
				&& StringUtils.isNotEmpty(orderInfo.optString("fieldName"))) {
			map.put("orderProperty", orderInfo.optString("fieldName")
					+ " " + orderInfo.optString("orderMethod"));
		}

		// 获取过滤信息
		JSONObject filterMap = json.optJSONObject("filterSet");
		if (filterMap != null) {
			map.putAll(filterMap.getParams());
		}
		return map;
	}

	private void requestParamProcess(Map<String, Object> requestParams) {
		String requestParamPreprocessorId = (String) requestParams
				.get("requestParamPreprocessorId");
		if (StringUtils.isNotEmpty(requestParamPreprocessorId)
				&& ObjectRegistry.containsBean(requestParamPreprocessorId)) {
			IRequestParamPreprocessor paramPreprocessor = ObjectRegistry
					.getBean(requestParamPreprocessorId,
							IRequestParamPreprocessor.class);
			paramPreprocessor.process(requestParams);
		}
	}

	/**
	 * 直接输出纯XML.
	 */
	public void renderXML(HttpServletResponse response, String xml) {
		try {
			response.setContentType("text/xml;charset=UTF-8");
			response.getWriter().write(xml);
		} catch (IOException e) {
			log.error(e.getMessage(), e);
		}
	}

	/**
	 * read json
	 * 
	 * @param req
	 * @return
	 */
	private JSONObject readJSONFormRequest(HttpServletRequest req) {
		String charset = req.getCharacterEncoding();
		JSONObject jsonObject = null;
		if (charset == null)
			charset = "UTF-8";

		try {
			BufferedReader in = new BufferedReader(new InputStreamReader(req
					.getInputStream(), charset));
			// Read the request
			CharArrayWriter data = new CharArrayWriter();
			char buf[] = new char[buf_size];
			int ret;
			while ((ret = in.read(buf, 0, buf_size)) != -1) {
				data.write(buf, 0, ret);
			}
			jsonObject = new JSONObject(data.toString());
		} catch (UnsupportedEncodingException e) {
			log.error(e);
		} catch (IOException e) {
			log.error(e);
		} catch (ParseException e) {
			log.error(e);
		}

		if (jsonObject != null && log.isDebugEnabled()) {
			log.debug("Received request:" + jsonObject.toString());
		}
		return jsonObject;
	}

	/**
	 * 预处理sql
	 * 
	 * @param prepareParams
	 *            查询参数
	 * @return 加工后的Sql
	 */
	protected DynamicSQL prepareStatement(String sqlKey,
			Map<String, Object> paramsMap) throws IllegalAccessException,
			InvocationTargetException, NoSuchMethodException {
		// 加工组装动态sql
		DynamicSQL dynamicSQL = SQLProvider.getDynamicSQL(sqlKey, paramsMap);
		return dynamicSQL;
	}

	/**
	 * 获取数据
	 * 
	 * @param sqlKey
	 *            sqlId
	 * @param envSqlId
	 *            环境变量取数SqlId
	 * @param json
	 *            查询条件、排序字段等
	 * @param requestParams
	 *            查询参数
	 * @return 数据包
	 * @throws Exception
	 */
	protected DataRows getDatas(String sqlKey, String envSqlId, JSONObject json,
			Map<String, Object> requestParams, boolean isNeedPaged)
			throws Exception {
		// 获取分页信息
		JSONObject limit = json.optJSONObject("limit");
		int page = limit.optInt("page");
		int pageSize = limit.optInt("size");
		int total = limit.optInt("total");
		Pager pager = new Pager(pageSize, page);

		// 实现数据查询
		BaseDao dao = ObjectRegistry.getBaseDao();
		List<Map<String, Object>> rows;
		// 获取sql
		DynamicSQL dynamicSQL = prepareStatement(sqlKey, requestParams);
		if (isNeedPaged) {
			rows = dao.pageQueryBySQL(pager, dynamicSQL.getSql(), dynamicSQL
					.getParams());
		} else {
			rows = dao.list(dynamicSQL.getSql(), dynamicSQL.getParams());
		}
		DataRows dataRows = new DataRows(rows, pager.getTotalRows());
		
		// 获取环境变量
		if (StringUtils.isNotEmpty(envSqlId)&&total==-1) {
			// 获取sql
			dynamicSQL = prepareStatement(envSqlId, requestParams);
			Map<String, Object> evnMap = dao.readMap(dynamicSQL.getSql(), dynamicSQL.getParams());
			dataRows.setEnv(evnMap);
		}
		
		return dataRows;
	}

	/**
	 * 加载xml数据
	 * 
	 * @param sqlKey
	 * @param configFile
	 * @param json
	 * @return
	 * @throws Exception
	 */
	protected String loadXMLData(String sqlKey, String envSqlId,
			InputStream configFile, JSONObject json,
			Map<String, Object> requestParams) throws Exception {
		// 读取Grid配置文件信息
		Head head = (Head) XMLSerializer.unserializeFromXMLStream(configFile);
		// 获取配置处理器，处理配置
		String configParamPreprocessorId = (String) requestParams
				.get("configParamPreprocessorId");
		if (StringUtils.isNotEmpty(configParamPreprocessorId)
				&& ObjectRegistry.containsBean(configParamPreprocessorId)) {
			IDhtmlxGridConfigProcessor dhtmlxGridConfigProcessor = ObjectRegistry
					.getBean(configParamPreprocessorId,
							IDhtmlxGridConfigProcessor.class);
			dhtmlxGridConfigProcessor.process(head);
		}
		String headXML = XMLSerializer.serializeToXML(head);

		Element rowsElement = XMLUtil.createElement("rows");
		rowsElement.add(XMLUtil.StringToNode(headXML).getDocument()
				.getRootElement());
		
		// 获取数据行
		DataRows dataRows = getDatas(sqlKey, envSqlId, json, requestParams, json
				.optJSONObject("limit").getBoolean("ifNeedPaged"));
		List<Map<String, Object>> rows = dataRows.getRows();

		// 获取数据处理器
		String dataProcessorId = (String) requestParams.get("dataProcessorId");
		IDhtmlxGridDataProcessor dhtmlxGridDataProcessor = null;
		if (StringUtils.isNotEmpty(dataProcessorId)
				&& ObjectRegistry.containsBean(dataProcessorId)) {
			dhtmlxGridDataProcessor = ObjectRegistry.getBean(dataProcessorId,
					IDhtmlxGridDataProcessor.class);
		}

		// 读取数据体
		int index = 1;
		for (Map<String, Object> row : rows) {
			Element rowElement = rowsElement.addElement("row");
			rowElement.addAttribute("id", String.valueOf(index++));
			// 加工行数据
			if (dhtmlxGridDataProcessor != null) {
				row = dhtmlxGridDataProcessor.process(row);
			}

			for (Column column : head.getColumns()) {
				Object fieldValue = 0;
				if (StringUtils.isNotEmpty(column.getId())) {
					fieldValue = row.get(column.getId().toUpperCase());
				}
				Element cellElement = XMLUtil.createElement("cell");
				cellElement.addText(BeanHelper.converToString(fieldValue));
				rowElement.add(cellElement);
			}
		}

		rowsElement.addAttribute("total_count", String.valueOf(dataRows
				.getSize()));
		
		// 读取自定义环境变量
		if (dataRows.getEnv() != null) {
			for (String key: dataRows.getEnv().keySet()) {
				Element rowElement = rowsElement.addElement("userdata");
				rowElement.addAttribute("name", key);
				rowElement.addText(String.valueOf(dataRows.getEnv().get(key)));
			}
		}

		Document doc = XMLUtil.createDocument(rowsElement);
		doc.setXMLEncoding("UTF-8");
		if (log.isDebugEnabled()) {
			XMLUtil.dump(doc, "GBK");
		}
		configFile.close();
		return doc.asXML();
	}
}

class DataRows {
	public DataRows(List<Map<String, Object>> rows, int size) {
		this.rows = rows;
		this.size = size;
	}

	private List<Map<String, Object>> rows;
	private int size;
	private Map<String, Object> env;

	public Map<String, Object> getEnv() {
		return env;
	}

	public void setEnv(Map<String, Object> env) {
		this.env = env;
	}

	public List<Map<String, Object>> getRows() {
		return rows;
	}

	public void setRows(List<Map<String, Object>> rows) {
		this.rows = rows;
	}

	public int getSize() {
		return size;
	}

	public void setSize(int size) {
		this.size = size;
	}
}
